package com.example.service;

import com.example.entity.TomcatServer;
import com.example.repository.TomcatRepository;
import com.example.utils.SSHUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.stream.Collectors;
import java.util.ArrayList;
import javax.annotation.PostConstruct;

@Service
public class FileService {
    
    private static final Logger logger = LoggerFactory.getLogger(FileService.class);
    private static final String REPORT_PATH = "webapps/fine-report/WEB-INF/reportlets";
    
    @Value("${ssh.jump.server.password}")
    private String serverPassword;
    
    @Value("${ssh.jump.server.host}")
    private String currentServerHost;
    
    @Value("${file.temp.dir:${java.io.tmpdir}}")
    private String tempDir;
    
    @PostConstruct
    public void init() {
        // 确保临时目录存在
        String mkdirCmd = String.format("mkdir -p %s/upload %s/download", tempDir.trim(), tempDir.trim());
        String chmodCmd = String.format("chmod -R 755 %s", tempDir.trim());
        
        try {
            Map<String, String> env = new HashMap<>();
            env.put("LANG", "en_US.UTF-8");
            env.put("LC_ALL", "en_US.UTF-8");
            
            // 直接执行本地命令
            executeCommand(mkdirCmd, currentServerHost);
            executeCommand(chmodCmd, currentServerHost);
            
            logger.info("Created and configured temporary directories under: {}", tempDir.trim());
            logger.info("Created upload directory: {}/upload", tempDir.trim());
            logger.info("Created download directory: {}/download", tempDir.trim());
        } catch (Exception e) {
            throw new RuntimeException("Failed to create and configure temporary directories: " + e.getMessage());
        }
    }
    
    private String createTempFilePath(String fileName, String subDir) {
        return String.format("%s/%s/%d_%s", tempDir.trim(), subDir, System.currentTimeMillis(), fileName);
    }
    
    @Autowired
    private TomcatRepository tomcatRepository;
    
    @Autowired
    private SSHUtil sshUtil;
    
    public void uploadFile(Long serverId, MultipartFile file, boolean overwrite) throws Exception {
        TomcatServer server = tomcatRepository.findById(serverId)
                .orElseThrow(() -> new RuntimeException("Server not found"));
        
        String targetPath = server.getServerPath() + "/" + REPORT_PATH;
        String fileName = file.getOriginalFilename();
        
        logger.info("Starting file upload process for file: {} to server: {} with overwrite: {}", 
            fileName, server.getHost(), overwrite);
        
        // 检查目标目录是否存在
        String checkDirCmd = String.format("[ -d \"%s\" ] && echo \"EXISTS\" || echo \"NOT_EXIST\"", targetPath);
        String dirCheck = executeCommand(checkDirCmd, server.getHost());
        logger.info("Directory check result: {}", dirCheck);
        
        if (dirCheck.contains("NOT_EXIST")) {
            // 创建目标目录
            String mkdirCmd = String.format("mkdir -p \"%s\" && chmod 755 \"%s\"", targetPath, targetPath);
            logger.info("Creating directory with command: {}", mkdirCmd);
            executeCommand(mkdirCmd, server.getHost());
        }
        
        // 检查文件是否已存在
        if (!overwrite && checkFileExists(serverId, fileName)) {
            throw new RuntimeException("File already exists and overwrite is not allowed");
        }
        
        // 检查目录权限
        String checkPermsCmd = String.format("ls -ld \"%s\"", targetPath);
        String perms = executeCommand(checkPermsCmd, server.getHost());
        logger.info("Directory permissions: {}", perms);
        
        // 创建临时文件
        String tempLocalFile = createTempFilePath(fileName, "upload");
        String tempRemoteFile = String.format("%s/.tmp_%d_%s", targetPath, System.currentTimeMillis(), fileName);
        
        try {
            // 将上传的文件保存到本地临时文件
            file.transferTo(new File(tempLocalFile));
            logger.info("Saved uploaded file to local temp file: {}", tempLocalFile);
            
            // 使用分块传输将文件上传到目标服务器
            uploadLargeFile(tempLocalFile, tempRemoteFile, server.getHost());
            logger.info("Uploaded file to remote temp file: {}", tempRemoteFile);
            
            // 检查临时文件大小
            String checkTempCmd = String.format("stat --format=%%s \"%s\"", tempRemoteFile);
            String tempSize = executeCommand(checkTempCmd, server.getHost()).trim();
            logger.info("Remote temp file size: {} bytes", tempSize);
            
            if (tempSize.isEmpty() || Long.parseLong(tempSize) == 0) {
                throw new RuntimeException("Uploaded file is empty");
            }
            
            // 移动到最终位置
            String targetFile = String.format("%s/%s", targetPath, fileName);
            String moveCmd = String.format("mv \"%s\" \"%s\" && chmod 644 \"%s\"", tempRemoteFile, targetFile, targetFile);
            logger.info("Moving temp file to final location");
            executeCommand(moveCmd, server.getHost());
            
            // 验证最终文件
            String verifyCmd = String.format("stat --format=%%s \"%s\"", targetFile);
            String finalSize = executeCommand(verifyCmd, server.getHost()).trim();
            logger.info("Final file size: {} bytes", finalSize);
            
            if (finalSize.isEmpty() || Long.parseLong(finalSize) == 0) {
                throw new RuntimeException("Final file is empty");
            }
            
            logger.info("File upload completed successfully: {}", fileName);
        } finally {
            // 清理本地临时文件
            try {
                new File(tempLocalFile).delete();
                // 清理整个上传临时目录
                cleanupTempFiles("upload");
            } catch (Exception e) {
                logger.error("Error cleaning up temp files: {}", e.getMessage());
            }
            
            // 清理远程临时文件
            try {
                String cleanupCmd = String.format("rm -f \"%s\"", tempRemoteFile);
                executeCommand(cleanupCmd, server.getHost());
            } catch (Exception e) {
                logger.error("Error cleaning up remote temp file: {}", e.getMessage());
            }
        }
    }
    
    public byte[] downloadFile(Long serverId, String fileName) throws Exception {
        TomcatServer server = tomcatRepository.findById(serverId)
                .orElseThrow(() -> new RuntimeException("Server not found"));
        
        String targetPath = server.getServerPath() + "/" + REPORT_PATH;
        String filePath = String.format("%s/%s", targetPath, fileName);
        
        // 检查文件是否存在
        String checkFileCmd = String.format("[ -f \"%s\" ] && echo \"EXISTS\" || echo \"NOT_EXIST\"", filePath);
        String fileCheck = executeCommand(checkFileCmd, server.getHost());
        logger.info("File check result: {}", fileCheck);
        
        if (!fileCheck.contains("EXISTS")) {
            throw new RuntimeException("File not found: " + fileName);
        }
        
        // 创建本地临时文件
        String tempLocalFile = createTempFilePath(fileName, "download");
        
        try {
            // 使用分块传输下载文件
            downloadLargeFile(filePath, tempLocalFile, server.getHost());
            logger.info("Downloaded file to local temp file: {}", tempLocalFile);
            
            // 读取文件内容
            byte[] content = Files.readAllBytes(new File(tempLocalFile).toPath());
            logger.info("Read {} bytes from downloaded file", content.length);
            
            return content;
        } finally {
            // 清理本地临时文件
            try {
                new File(tempLocalFile).delete();
                // 清理整个下载临时目录
                cleanupTempFiles("download");
            } catch (Exception e) {
                logger.error("Error cleaning up temp files: {}", e.getMessage());
            }
        }
    }
    
    public Page<String> listFiles(Long serverId, Pageable pageable, String search) throws Exception {
        TomcatServer server = tomcatRepository.findById(serverId)
                .orElseThrow(() -> new RuntimeException("Server not found"));
        
        String targetPath = server.getServerPath() + "/" + REPORT_PATH;
        
        // 检查目录是否存在
        String checkDirCmd = String.format("[ -d \"%s\" ] && echo \"EXISTS\" || echo \"NOT_EXIST\"", targetPath);
        String dirCheck = executeCommand(checkDirCmd, server.getHost());
        logger.info("Directory check result: {}", dirCheck);
        
        if (dirCheck.contains("NOT_EXIST")) {
            logger.warn("Directory does not exist: {}", targetPath);
            return new PageImpl<>(Arrays.asList());
        }
        
        // 使用ls命令列出文件，如果有搜索条件则使用grep过滤
        String listCmd;
        if (search != null && !search.trim().isEmpty()) {
            // 使用find命令支持文件名搜索，-iname支持大小写不敏感搜索
            listCmd = String.format("find \"%s\" -maxdepth 1 -type f -iname \"*%s*\" -printf \"%%f\\n\" | sort", 
                targetPath, search.trim().replace("\"", ""));
        } else {
            listCmd = String.format("ls -1 \"%s\" | sort", targetPath);
        }
        
        String result = executeCommand(listCmd, server.getHost());
        logger.info("File listing result: {}", result);
        
        List<String> allFiles = Arrays.stream(result.split("\n"))
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .collect(Collectors.toList());
        
        // 计算总数和分页
        int total = allFiles.size();
        int start = (int) pageable.getOffset();
        int end = Math.min((start + pageable.getPageSize()), total);
        
        // 返回分页结果
        List<String> pageContent = start < end ? allFiles.subList(start, end) : new ArrayList<>();
        return new PageImpl<>(pageContent, pageable, total);
    }
    
    public boolean checkFileExists(Long serverId, String fileName) throws Exception {
        TomcatServer server = tomcatRepository.findById(serverId)
                .orElseThrow(() -> new RuntimeException("Server not found"));
        
        String targetPath = server.getServerPath() + "/" + REPORT_PATH;
        String filePath = String.format("%s/%s", targetPath, fileName);
        
        String checkFileCmd = String.format("[ -f \"%s\" ] && echo \"EXISTS\" || echo \"NOT_EXIST\"", filePath);
        String fileCheck = executeCommand(checkFileCmd, server.getHost());
        logger.info("File check result for {}: {}", fileName, fileCheck);
        
        return fileCheck.contains("EXISTS");
    }
    
    private String executeCommand(String command, String host) throws Exception {
        Map<String, String> env = new HashMap<>();
        env.put("LANG", "en_US.UTF-8");
        env.put("LC_ALL", "en_US.UTF-8");
        
        // 只有当目标主机不是当前主机时，才使用SSH
        if (!host.equals(currentServerHost)) {
            env.put("SSHPASS", serverPassword);
            // 修改SSH命令格式，使用-p参数直接传递密码
            command = String.format(
                "sshpass -e ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o PreferredAuthentications=password -o PubkeyAuthentication=no root@%s '%s'",
                host,
                command.replace("'", "'\\''")
            );
        }
        
        logger.info("Executing command: {}", command);
        String result = sshUtil.executeCommand(command, env);
        logger.debug("Command result: {}", result);
        return result;
    }

    // 添加新方法用于处理大文件的分块传输
    private void uploadLargeFile(String sourceFile, String targetFile, String host) throws Exception {
        if (host.equals(currentServerHost)) {
            // 本地文件复制
            String cpCmd = String.format("cp \"%s\" \"%s\"", sourceFile, targetFile);
            executeCommand(cpCmd, currentServerHost);
        } else {
            // 远程文件传输使用sshpass和base64编码
            String base64Cmd = String.format(
                "cat \"%s\" | base64 | sshpass -p '%s' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o PreferredAuthentications=password -o PubkeyAuthentication=no root@%s 'base64 -d > \"%s\"'",
                sourceFile, serverPassword, host, targetFile
            );
            Map<String, String> env = new HashMap<>();
            env.put("LANG", "en_US.UTF-8");
            env.put("LC_ALL", "en_US.UTF-8");
            sshUtil.executeCommand(base64Cmd, env);
            
            // 验证文件是否上传成功
            String checkCmd = String.format(
                "sshpass -p '%s' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o PreferredAuthentications=password -o PubkeyAuthentication=no root@%s '[ -f \"%s\" ] && stat -c %%s \"%s\" || echo \"0\"'",
                serverPassword, host, targetFile, targetFile
            );
            String size = sshUtil.executeCommand(checkCmd, env).trim();
            if ("0".equals(size)) {
                throw new RuntimeException("Failed to upload file");
            }
        }
    }

    // 添加新方法用于处理大文件的下载
    private void downloadLargeFile(String sourceFile, String targetFile, String host) throws Exception {
        if (host.equals(currentServerHost)) {
            // 本地文件复制
            String cpCmd = String.format("cp \"%s\" \"%s\"", sourceFile, targetFile);
            executeCommand(cpCmd, currentServerHost);
        } else {
            // 远程文件下载，使用sshpass和base64编码
            String base64Cmd = String.format(
                "sshpass -p '%s' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o PreferredAuthentications=password -o PubkeyAuthentication=no root@%s 'cat \"%s\" | base64' | base64 -d > \"%s\"",
                serverPassword, host, sourceFile, targetFile
            );
            Map<String, String> env = new HashMap<>();
            env.put("LANG", "en_US.UTF-8");
            env.put("LC_ALL", "en_US.UTF-8");
            sshUtil.executeCommand(base64Cmd, env);
            
            // 验证文件是否下载成功
            File file = new File(targetFile);
            if (!file.exists() || file.length() == 0) {
                throw new RuntimeException("Failed to download file");
            }
        }
    }

    // 添加定期清理临时文件的方法
    @Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行
    public void cleanupTempFiles() {
        logger.info("Starting temporary files cleanup");
        
        // 清理超过24小时的文件
        long cutoff = System.currentTimeMillis() - (24 * 60 * 60 * 1000);
        
        cleanDirectory(new File(tempDir, "upload"), cutoff);
        cleanDirectory(new File(tempDir, "download"), cutoff);
        
        logger.info("Completed temporary files cleanup");
    }
    
    private void cleanDirectory(File directory, long cutoff) {
        try {
            // 删除超过指定时间的文件
            String cleanupCmd = String.format("find %s -type f -mtime +1 -delete", directory.getAbsolutePath());
            Map<String, String> env = new HashMap<>();
            env.put("LANG", "en_US.UTF-8");
            env.put("LC_ALL", "en_US.UTF-8");
            
            sshUtil.executeCommand(cleanupCmd, env);
            logger.info("Cleaned up old files in directory: {}", directory.getAbsolutePath());
        } catch (Exception e) {
            logger.error("Error cleaning up directory {}: {}", directory.getAbsolutePath(), e.getMessage());
        }
    }

    private void cleanupTempFiles(String subDir) {
        try {
            // 删除子目录中的所有文件
            String cleanupCmd = String.format("find %s/%s -type f -delete", tempDir, subDir);
            Map<String, String> env = new HashMap<>();
            env.put("LANG", "en_US.UTF-8");
            env.put("LC_ALL", "en_US.UTF-8");
            
            sshUtil.executeCommand(cleanupCmd, env);
            logger.info("Cleaned up temporary files in {}/{}", tempDir, subDir);
        } catch (Exception e) {
            logger.error("Error cleaning up temp files in {}/{}: {}", tempDir, subDir, e.getMessage());
        }
    }

    // 修改定期清理方法为每6小时执行一次，作为额外保障
    @Scheduled(cron = "0 0 */6 * * ?")
    public void scheduledCleanupTempFiles() {
        logger.info("Starting scheduled temporary files cleanup");
        try {
            cleanupTempFiles("upload");
            cleanupTempFiles("download");
            logger.info("Completed scheduled temporary files cleanup");
        } catch (Exception e) {
            logger.error("Error during scheduled cleanup: {}", e.getMessage());
        }
    }
} 